#include "common.h"

#ifdef USE_DISP

#include "disp.h"
#include "clock.h"
#include <math.h>
#include "serial.h"
#include <string.h>

#ifdef SDCC
#include <malloc.h>
extern void * memset(void * buf,unsigned char ch ,size_t count);
extern void * memcpy(void * dst,void * src,size_t acount);
#endif

SBIT(DISPPORTDAT,0x90,2);//sbit DISPPORTDAT=P1^2;
SBIT(DISPPORTCLK,0x90,0);//sbit DISPPORTCLK=P1^0;

#define DISPINTERVAL 2//2ms
#define DISPLAY_BANK_ROW 7
#define DISPLAY_BANK_COLUMN 5
#define DISPLAY_BANK 4
#define DISPLAY_SPLIT_COLUMN 2
#define DISPLAY_BANK_WIDTH (DISPLAY_BANK_COLUMN+DISPLAY_SPLIT_COLUMN)
#define DISPLAY_ALL_COLUMN (DISPLAY_BANK_WIDTH*DISPLAY_BANK-DISPLAY_SPLIT_COLUMN)
static volatile uchar *framebuf;

#ifdef DISP_NUMBER_ONLY
#define DISP_CHAR_BASE	'0'//48
#define DISP_CHAR_MAX ('9'+1)
static unsigned char volatile code charRaw[]={
		4, 0xC1,0xAE,0xB6,0xC1,			//0
		2, 0xFD,0x80,					//1
		4, 0x9D,0xAE,0xB6,0xB9,			//2
		4, 0xDD,0xB6,0xB6,0xC9,			//3
		4, 0xC7,0xD9,0x80,0xDF,			//4
		4, 0xD0,0xB6,0xB6,0xCE,			//5
		4, 0xC1,0xB6,0xB6,0xCD,			//6
		4, 0xFE,0x86,0xFA,0xFC,			//7
		4, 0xC9,0xB6,0xB6,0xC9,			//8
		4, 0xD9,0xB6,0xB6,0xC1,			//9
		4, 0x80,0xBE,0xBE,0x80,			//na
};
static unsigned int volatile code charMap[DISP_CHAR_MAX-DISP_CHAR_BASE+1]={
		0,5,8,13,18,23,28,33,38,43,48
};
#else
#define DISP_CHAR_BASE	' '//32
#define DISP_CHAR_MAX 127
static unsigned char volatile code charRaw[]={
		3, 0xFF,0xFF,0xFF,				/* */
		1, 0xA1,						//!
		3, 0xFC,0xFF,0xFC,				//"
		5, 0xEB,0xC1,0xEB,0xC1,0xEB,	//#
		4, 0xD1,0x85,0xD0,0xC5,			//$
		4, 0x9C,0xE7,0xF9,0x8E,			//%
		4, 0xCD,0xB2,0xCD,0xB7,			//&
		2, 0xFB,0xFC,					//'

		3, 0xE3,0xDD,0xBE,				//(
		3, 0xBE,0xDD,0xE3,				//)
		5, 0xD5,0xE3,0xC1,0xE3,0xD5,	//*
		3, 0xF7,0xC1,0xF7,				//+
		2, 0xBF,0xCF,					//,
		3, 0xF7,0xF7,0xF7,				//-
		1, 0xDF,						//.
		4, 0x9F,0xE7,0xF9,0xFE,			///

		4, 0xC1,0xAE,0xB6,0xC1,			//0
		2, 0xFD,0x80,					//1
		4, 0x9D,0xAE,0xB6,0xB9,			//2
		4, 0xDD,0xB6,0xB6,0xC9,			//3
		4, 0xC7,0xD9,0x80,0xDF,			//4
		4, 0xD0,0xB6,0xB6,0xCE,			//5
		4, 0xC1,0xB6,0xB6,0xCD,			//6
		4, 0xFE,0x86,0xFA,0xFC,			//7

		4, 0xC9,0xB6,0xB6,0xC9,			//8
		4, 0xD9,0xB6,0xB6,0xC1,			//9
		1, 0xD7,						//:
		2, 0xBF,0xD7,					//;
		3, 0xF7,0xEB,0xDD,				//<
		3, 0xD7,0xD7,0xD7,				//=
		3, 0xDD,0xEB,0xF7,				//>
		4, 0xFD,0xAE,0xF6,0xF9,			//?

		5, 0xC1,0xAE,0xAA,0x82,0x81,	//@
		4, 0x81,0xEE,0xEE,0x81,			//A
		4, 0x80,0xB6,0xB6,0xC9,			//B
		4, 0xC1,0xBE,0xBE,0xDD,			//C
		4, 0x80,0xBE,0xBE,0xC1,			//D
		4, 0x80,0xB6,0xB6,0xBE,			//E
		4, 0x80,0xF6,0xF6,0xFE,			//F
		4, 0xC1,0xBE,0xB6,0xC5,			//G

		4, 0x80,0xF7,0xF7,0x80,			//H
		3, 0xBE,0x80,0xBE,				//I
		4, 0xDF,0xBF,0xBE,0xC0,			//J
		4, 0x80,0xF7,0xEB,0x9D,			//K
		4, 0x80,0xBF,0xBF,0xBF,			//L
		5, 0x80,0xF3,0x8F,0xF3,0x80,	//M
		4, 0x80,0xF3,0xE7,0x80,			//N
		4, 0xC1,0xBE,0xBE,0xC1,			//O

		4, 0x80,0xF6,0xF6,0xF9,			//P
		5, 0xC1,0xBE,0xAE,0xC1,0xDF,	//Q
		4, 0x80,0xF6,0xE6,0x99,			//R
		4, 0xD9,0xB6,0xB6,0xCD,			//S
		5, 0xFE,0xFE,0x80,0xFE,0xFE,	//T
		4, 0xC0,0xBF,0xBF,0xC0,			//U
		5, 0xF8,0xC7,0xBF,0xC7,0xF8,	//V
		5, 0xF0,0x8F,0xE1,0x8F,0xF0,	//W

		5, 0x9C,0xEB,0xF7,0xEB,0x9C,	//X
		5, 0xFC,0xF3,0x8F,0xF3,0xFC,	//Y
		4, 0x9E,0xA6,0xBA,0xBC,			//Z
		2, 0x80,0xBE,					//[
		4, 0xFE,0xF9,0xE7,0x9F,			/*\*/
		2, 0xBE,0x80,					//]
		3, 0xFD,0xFE,0xFD,				//^
		4, 0xBF,0xBF,0xBF,0xBF,			//_

		2, 0xFE,0xFD,					//`
		3, 0x8B,0xAB,0x87,				//a
		3, 0x83,0xAF,0x8F,				//b
		3, 0xC7,0xBB,0xBB,				//c
		3, 0x8F,0xAF,0x83,				//d
		3, 0x87,0xAB,0xA3,				//e
		3, 0xEF,0x83,0xEB,				//f
		3, 0xA7,0xAB,0x83,				//g

		3, 0x83,0xEF,0x8F,				//h
		1, 0x8B,						//i
		2, 0xBF,0xCB,					//j
		3, 0x83,0xEF,0x97,				//k
		2, 0x83,0xBF,					//l
		5, 0x83,0xFB,0x83,0xFB,0x87,	//m
		3, 0x83,0xFB,0x87,				//n
		3, 0xC7,0xBB,0xC7,				//o

		3, 0x83,0xEB,0xE7,				//p
		3, 0xE7,0xEB,0x83,				//q
		3, 0x83,0xF7,0xFB,				//r
		3, 0xA7,0xAB,0xCB,				//s
		3, 0xF7,0x83,0xB7,				//t
		3, 0xC3,0xBF,0x83,				//u
		3, 0xC3,0xBF,0xC3,				//v
		5, 0xC3,0xBF,0xC7,0xBF,0xC3,	//w

		4, 0x9B,0xE7,0xCF,0xB3,			//x
		3, 0xA3,0xAF,0xC3,				//y
		3, 0x9B,0xAB,0xB3,				//z
		3, 0xF7,0xC1,0xBE,				//{
		1, 0x80,						//|
		3, 0xBE,0xC1,0xF7,				//}
		4, 0xEF,0xF7,0xF7,0xFB,			//~
		4, 0x80,0xBE,0xBE,0x80,			//na
};
static unsigned int volatile code charMap[DISP_CHAR_MAX-DISP_CHAR_BASE+1]={
		0,		4,		6,		10,	16,	21,	26,	31,	34,	38,	42,	48,	52,	55,	59,	61,
		66,	71,	74,	79,	84,	89,	94,	99,	104,	109,	114,	116,	119,	123,	127,	131,
		136,	142,	147,	152,	157,	162,	167,	172,	177,	182,	186,	191,	196,	201,	207,	212,
		217,	222,	228,	233,	238,	244,	249,	255,	261,	267,	273,	278,	281,	286,	289,	293,
		298,	301,	305,	309,	313,	317,	321,	325,	329,	333,	335,	338,	342,	345,	351,	355,
		359,	363,	367,	371,	375,	379,	383,	387,	393,	398,	402,	406,	410,	412,	416,	421
};
#endif

#define DISPLAY_ARROW_TOTAL 8
#define DISPLAY_ARROW_BANK_COLUMN 5
static unsigned char volatile code arrowRaw[DISPLAY_ARROW_TOTAL][DISPLAY_ARROW_BANK_COLUMN]= {
		{0xF7,0xFB,0xC1,0xFB,0xF7},	//up
		{0xDF,0xED,0xF5,0xF9,0xE1},	//up-right
		{0xF7,0xF7,0xD5,0xE3,0xF7},	//right
		{0xFD,0xDB,0xD7,0xCF,0xC3},	//down-right
		{0xF7,0xEF,0xC1,0xEF,0xF7},	//down
		{0xC3,0xCF,0xD7,0xDB,0xFD},	//down-left
		{0xF7,0xE3,0xD5,0xF7,0xF7},	//left
		{0xE1,0xF9,0xF5,0xED,0xDF},	//up-left
};

static volatile uchar linenum;
static volatile uint disptimer;
static volatile bool isworking;

//following functions are used for string race horse display
#ifndef DISP_NO_RACE_HORSE
static volatile uchar *dispstring;
static volatile uchar dispStrCurChar=0;
static volatile uchar dispStrCharLen=0;
static volatile uchar dispStrCurCharColum=0;
static volatile uchar dispStrBankStart=0;
static volatile uchar dispStrBankEnd=DISPLAY_BANK;
static volatile bool isracing=false;
static volatile uchar dispStrSpeed=30;
static volatile uchar dispStrSpeedCounter=0;
#else
#ifdef SDCC
	#pragma disable_warning 85//disable warning 85: unreferenced function argument:xxx
#endif
#endif

void DISP_INIT()
{
#ifdef DEVELOP_PHASE
	uchar charId=0;
	uint posRaw=0;
	uchar *pRaw;
	bool isTableValid=true;
#endif
#if !USE_HEAP
	static volatile uchar xdata _framebuf[DISPLAY_ALL_COLUMN];
#ifndef DISP_NO_RACE_HORSE
	static volatile uchar xdata _dispstring[DISP_STRING_SIZE];
#endif
#endif
	CLK_ADDCLK(&disptimer);
	isworking=false;

	//create framebuffer for whole display panel
#if USE_HEAP
	framebuf=(uchar*)malloc(DISPLAY_ALL_COLUMN*sizeof(uchar));
#ifndef DISP_NO_RACE_HORSE
	dispstring=(uchar*)malloc(DISP_STRING_SIZE*sizeof(uchar));
#endif
#else
	framebuf=_framebuf;
#ifndef DISP_NO_RACE_HORSE
	dispstring=_dispstring;
#endif
#endif
	DISP_CLS();

#ifdef DEVELOP_PHASE
	//check charMap according to charRaw
	pRaw=(uchar*)charRaw;
	for(charId=0;charId<DISP_CHAR_MAX-DISP_CHAR_BASE+1;charId++) {
		if(charMap[charId]!=posRaw) {
			isTableValid=false;
			break;
		}
		posRaw+=*pRaw;
		posRaw++;
		pRaw+=*pRaw;
		pRaw++;
	}
	if(!isTableValid) {
		CLK_ENABLE();
		DISP_ENABLE();
		EA=1;
		DISP_NATIVE_SHOWERROR(0);
		while(1) {DISP_QUERY();}//die here
	}
#endif
}

void DISP_ENABLE()
{
	disptimer=0;
	linenum=0;
	isworking=true;
}

void DISP_CLS_RANGE(uchar bankstart, uchar bankend) {
	uchar *p=framebuf;
	if(bankstart>=DISPLAY_BANK || bankend>=DISPLAY_BANK || bankstart>bankend ) return;

	memset(p+bankstart*DISPLAY_BANK_WIDTH,0xFF,((uchar)(bankend-bankstart)*DISPLAY_BANK_WIDTH+DISPLAY_BANK_COLUMN)*sizeof(uchar));

}

void DISP_CLS()
{
	DISP_CLS_RANGE(0,DISPLAY_BANK-1);
}

void DISP_DISABLE()
{
	DISP_CLS();
	isworking=false;
}

uchar reform(uchar din)
{
	uchar retv=0;
	if(din&0x80) retv|=0x10;
	if(din&0x40) retv|=0x20;
	if(din&0x20) retv|=0x08;
	if(din&0x10) retv|=0x40;
	if(din&0x08) retv|=0x80;
	return retv;
}

uchar* getCharRawPointer(uchar ch) {
	const uchar* p;
	if(ch>=(uchar)DISP_CHAR_BASE && ch<=(uchar)DISP_CHAR_MAX) {
		ch-=DISP_CHAR_BASE;
	}
	else {
		ch=DISP_CHAR_MAX;
	}
	p=(const uchar*)(&(charRaw[charMap[ch]]));
	return p;
}

#define getCharWidth(p) *(p)
#define getCharPointer(p) (uchar*)((p)+1)

void DISP_QUERY()
{
	if(isworking==true)
	{
		if(disptimer>=DISPINTERVAL)
		{
#ifndef DISP_NO_RACE_HORSE
			//is racing and need update race horse display banks?
			if(isracing && ++dispStrSpeedCounter>=dispStrSpeed) {
				uchar *p=framebuf+dispStrBankStart*DISPLAY_BANK_WIDTH;
				uchar width=(dispStrBankEnd-dispStrBankStart)*DISPLAY_BANK_WIDTH+DISPLAY_BANK_COLUMN;
				uchar *pCharRaw=getCharRawPointer(*(dispstring+dispStrCurChar));
				uchar charWidth=getCharWidth(pCharRaw);
				uchar *pCharPointer=getCharPointer(pCharRaw);

				memcpy(p,p+1,(width-1)*sizeof(uchar));
				p+=(width-1)*sizeof(uchar);

				//shift in blank column if current character has been shifted in the display range
				if(dispStrCurCharColum>=charWidth) {
					*p=0xFF;
					dispStrCurCharColum=0;
					dispStrCurChar++;
					if(dispStrCurChar>=dispStrCharLen) {
						dispStrCurChar=0;
					}
				}
				else {
					*p=*(pCharPointer+dispStrCurCharColum);
					dispStrCurCharColum++;
				}

				dispStrSpeedCounter=0;
			}
#endif
			DISP_REFRESHONELINE();
			disptimer=0;
		}
	}
}

void DISP_OUTPUTCHAR(uchar val)
{
		ACC=val;

#ifdef SDCC
		#define DISPPORTCLK _DISPPORTCLK
		#define DISPPORTDAT _DISPPORTDAT
		__asm
#else
		#pragma asm
#endif

		RRC A
		CLR DISPPORTCLK
		MOV DISPPORTDAT,C
		SETB DISPPORTCLK
	
		RRC A
		CLR DISPPORTCLK
		MOV DISPPORTDAT,C
		SETB DISPPORTCLK
	
		RRC A
		CLR DISPPORTCLK
		MOV DISPPORTDAT,C
		SETB DISPPORTCLK
	
		RRC A
		CLR DISPPORTCLK
		MOV DISPPORTDAT,C
		SETB DISPPORTCLK
	
		RRC A
		CLR DISPPORTCLK
		MOV DISPPORTDAT,C
		SETB DISPPORTCLK
	
		RRC A
		CLR DISPPORTCLK
		MOV DISPPORTDAT,C
		SETB DISPPORTCLK
	
		RRC A
		CLR DISPPORTCLK
		MOV DISPPORTDAT,C
		SETB DISPPORTCLK
		
		RRC A
		CLR DISPPORTCLK
		MOV DISPPORTDAT,C
		SETB DISPPORTCLK

#ifdef SDCC
		__endasm;
		#undef DISPPORTCLK
		#undef DISPPORTDAT
#else
		#pragma endasm
#endif

}

uchar getCurrentLineBuffer(uchar bank) {
	uchar ret=0,i;
	uchar *p=framebuf;
	bank=bank%DISPLAY_BANK;
	p+=bank*DISPLAY_BANK_WIDTH;
	for(i=7;i>=7-DISPLAY_BANK_COLUMN;i--) {
		if((*p++)&(1<<(linenum))) ret|=1<<i;
	}
	return ret;
}

void DISP_REFRESHONELINE()
{
	//display select table -- 7-7logic lines; [][0]-physical line selector in which bank; [][1]-physical bit position in the bank
	static unsigned char volatile code linetable[7][2]={
		{0,2},{1,4},{1,2},{2,2},{3,4},{2,4},{3,2}
	};
	uchar fb[DISPLAY_BANK];//each display line contents for 4 display banks
	uchar i;
	for(i=0;i<DISPLAY_BANK;i++) {
		fb[i]=reform(getCurrentLineBuffer(i));
	}
	//open line selector
	fb[linetable[linenum][0]]|=linetable[linenum][1];

	for(i=0;i<DISPLAY_BANK;i++) {
		DISP_OUTPUTCHAR(fb[i]);
	}
	
	linenum++;
	linenum%=DISPLAY_BANK_ROW;
}

void DISP_NATIVE_SHOWERROR(uchar errorId) {
	static unsigned char volatile code char_E[4]={0x80,0xB6,0xB6,0xBE};
	static unsigned char volatile code char_r[4]={0xFF,0x83,0xF7,0xFB};
#ifdef DISP_SIMPLIFY_ERROR_CODE
	uchar col;
#else
	static unsigned char volatile code char_Num[][4]={
		{0xC1,0xAE,0xB6,0xC1},	//0
		{0xFF,0xFD,0x80,0xFF},	//1
		{0x9D,0xAE,0xB6,0xB9},	//2
		{0xDD,0xB6,0xB6,0xC9},	//3
		{0xC7,0xD9,0x80,0xDF},	//4
		{0xD0,0xB6,0xB6,0xCE},	//5
		{0xC1,0xB6,0xB6,0xCD},	//6
		{0xFE,0x86,0xFA,0xFC},	//7
		{0xC9,0xB6,0xB6,0xC9},	//8
		{0xD9,0xB6,0xB6,0xC1},	//9
		{0x81,0xEE,0xEE,0x81},	//A
		{0x80,0xB6,0xB6,0xC9},	//B
		{0xC1,0xBE,0xBE,0xDD},	//C
		{0x80,0xBE,0xBE,0xC1},	//D
		{0x80,0xB6,0xB6,0xBE},	//E
		{0x80,0xF6,0xF6,0xFE},	//F
	};
#endif
	uchar *p=framebuf;
	memcpy(p,char_E,sizeof(char_E));
	p+=DISPLAY_BANK_WIDTH;
	memcpy(p,char_r,sizeof(char_r));
	p+=DISPLAY_BANK_WIDTH;
	memcpy(p,char_r,sizeof(char_r));
	p+=DISPLAY_BANK_WIDTH;
#ifdef DISP_SIMPLIFY_ERROR_CODE
	errorId=errorId%(DISPLAY_BANK_ROW*DISPLAY_BANK_COLUMN);
	for(col=0;col<errorId/(uchar)DISPLAY_BANK_ROW;col++) {
			*p++=0xFF<<DISPLAY_BANK_ROW;
	}
	*p=0xFF<<(errorId%DISPLAY_BANK_ROW+1);
#else
	errorId%=sizeof(char_Num)/sizeof(char_Num[0]);
	memcpy(p,char_Num[errorId],sizeof(char_Num[errorId]));
#endif
}

void DISP_RACE_SPEED(uchar speed) {
#ifndef DISP_NO_RACE_HORSE
	dispStrSpeed=speed;
#endif
}

void DISP_RESET_RACE() {
#ifndef DISP_NO_RACE_HORSE
	isracing=false;
	dispStrCurChar=0;
	dispStrCurCharColum=0;
	dispStrBankStart=0;
	dispStrBankEnd=0;
	dispStrSpeedCounter=0;
#endif
}

void DISP_SET_S(uchar bankstart, uchar bankend, uchar *str) {
#ifndef DISP_NO_RACE_HORSE
#define STR_APPEND_SPACE "  "
	uchar i=strlen(str);
	if(bankstart>=DISPLAY_BANK || bankend>=DISPLAY_BANK || bankstart>bankend ) return;
	if(i>DISP_STRING_SIZE-sizeof(STR_APPEND_SPACE)) return;

	//stop racing if it is racing
	if(isracing) {
		DISP_CLS_RANGE(dispStrBankStart,dispStrBankEnd);
	}
	DISP_RESET_RACE();

	//copy str
	memcpy(dispstring,str,i);
	//append space
	memcpy(dispstring+i,STR_APPEND_SPACE,sizeof(STR_APPEND_SPACE));
	dispStrCharLen=i+sizeof(STR_APPEND_SPACE);

	//cls range
	DISP_CLS_RANGE(bankstart,bankend);

	//init racing configuration
	dispStrCurChar=0;
	dispStrCurCharColum=0;
	dispStrBankStart=bankstart;
	dispStrBankEnd=bankend;
	isracing=true;
#endif
	return;
}

void DISP_SET(uchar pos, uchar val)
{
	uchar *p=framebuf;
	uchar *pCharRaw=getCharRawPointer(val);
	uchar width=getCharWidth(pCharRaw);
	uchar *pCharPointer=getCharPointer(pCharRaw);
	pos%=DISPLAY_BANK;

#ifndef DISP_NO_RACE_HORSE
	//stop racing if the DISP_SET is in racing range
	if(isracing && dispStrBankStart<=pos && pos<=dispStrBankEnd) {
		DISP_CLS_RANGE(dispStrBankStart,dispStrBankEnd);
		DISP_RESET_RACE();
	}
#endif

	p+=pos*DISPLAY_BANK_WIDTH;

	memset(p,0xFF,DISPLAY_BANK_COLUMN*sizeof(uchar));

	p+=(DISPLAY_BANK_COLUMN-width)>>1;
	memcpy(p,pCharPointer,width*sizeof(uchar));
}

void DISP_SET_NUMBER(uint num) {
	uchar t;
	t=num%10;num/=10;
	DISP_SET(3,t+'0');
	t=num%10;num/=10;
	DISP_SET(2,t+'0');
	t=num%10;num/=10;
	DISP_SET(1,t+'0');
	t=num%10;
	DISP_SET(0,t+'0');
}

void DISP_SET_ARROW(uchar pos, uchar val)
{
	uchar *p=framebuf;
	pos%=DISPLAY_BANK;
	val%=DISPLAY_ARROW_TOTAL;

#ifndef DISP_NO_RACE_HORSE
	//stop racing if the DISP_SET is in racing range
	if(isracing && dispStrBankStart<=pos && pos<=dispStrBankEnd) {
		DISP_CLS_RANGE(dispStrBankStart,dispStrBankEnd);
		DISP_RESET_RACE();
	}
#endif

	p+=pos*DISPLAY_BANK_WIDTH;
	memcpy(p,arrowRaw[val],DISPLAY_ARROW_BANK_COLUMN*sizeof(uchar));
}
#endif

